'use strict';

/**
 * 0.1.1 General-purpose jQuery wrapper. Simply pass the plugin name as the
 * expression.
 * 
 * It is possible to specify a default set of parameters for each jQuery plugin.
 * Under the jq key, namespace each plugin by that which will be passed to
 * ui-jq. Unfortunately, at this time you can only pre-define the first
 * parameter.
 * 
 * @example { jq : { datepicker : { showOn:'click' } } }
 * 
 * @param ui-jq
 *            {string} The $elm.[pluginName]() to call.
 * @param [ui-options]
 *            {mixed} Expression to be evaluated and passed as options to the
 *            function Multiple parameters can be separated by commas
 * @param [ui-refresh]
 *            {expression} Watch expression and refire plugin on changes
 * 
 * @example <input ui-jq="datepicker"
 *          ui-options="{showOn:'click'},secondParameter,thirdParameter"
 *          ui-refresh="iChange">
 */
angular
		.module('ui.jq', [ 'ui.load' ])
		.value('uiJqConfig', {})
		.directive(
				'uiJq',
				[
						'uiJqConfig',
						'JQ_CONFIG',
						'uiLoad',
						'$timeout',
						function uiJqInjectingFunction(uiJqConfig, JQ_CONFIG,
								uiLoad, $timeout) {

							return {
								restrict : 'A',
								compile : function uiJqCompilingFunction(tElm,
										tAttrs) {

									if (!angular.isFunction(tElm[tAttrs.uiJq])
											&& !JQ_CONFIG[tAttrs.uiJq]) {
										throw new Error('ui-jq: The "'
												+ tAttrs.uiJq
												+ '" function does not exist');
									}
									var options = uiJqConfig
											&& uiJqConfig[tAttrs.uiJq];

									return function uiJqLinkingFunction(scope,
											elm, attrs) {

										function getOptions() {
											var linkOptions = [];

											// If ui-options are passed, merge
											// (or override) them onto global
											// defaults and pass to the jQuery
											// method
											if (attrs.uiOptions) {
												linkOptions = scope
														.$eval('['
																+ attrs.uiOptions
																+ ']');
												if (angular.isObject(options)
														&& angular
																.isObject(linkOptions[0])) {
													linkOptions[0] = angular
															.extend(
																	{},
																	options,
																	linkOptions[0]);
												}
											} else if (options) {
												linkOptions = [ options ];
											}
											return linkOptions;
										}

										// If change compatibility is enabled,
										// the form input's "change" event will
										// trigger an "input" event
										if (attrs.ngModel
												&& elm
														.is('select,input,textarea')) {
											elm.bind('change', function() {
												elm.trigger('input');
											});
										}

										// Call jQuery method and pass relevant
										// options
										function callPlugin() {
											$timeout(function() {
												elm[attrs.uiJq].apply(elm,
														getOptions());
											}, 0, false);
										}

										function refresh() {
											// If ui-refresh is used, re-fire
											// the the method upon every change
											if (attrs.uiRefresh) {
												scope.$watch(attrs.uiRefresh,
														function() {
															callPlugin();
														});
											}
										}

										if (JQ_CONFIG[attrs.uiJq]) {
											uiLoad.load(JQ_CONFIG[attrs.uiJq])
													.then(function() {
														callPlugin();
														refresh();
													});
										} else {
											callPlugin();
											refresh();
										}
									};
								}
							};
						} ]);